// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#include "coin2/exchange/bybit_futures/api_util/auth_key.h"

#include <boost/algorithm/string/replace.hpp>
#include <list>

#include <openssl/hmac.h>
#include <openssl/sha.h>

#include "coin2/base/time.h"
#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/json_writer.h"

std::string BybitBaseAuth::GetCurrTimeSeconds() {
  return std::to_string(GetCurrentTimestamp() / 1000000);
}

std::string BybitBaseAuth::GetSignature(
    const std::string& access_key,
    const std::string& secret_key,
    const std::string& timestamp,
    const std::string& param) {
  if (access_key.empty() || secret_key.empty()) {
    throw std::runtime_error("[Bybit] API key and secret key are required");
  }
  std::string cre;
  if (param.empty()) {
    cre = "api_key=" + access_key + "&timestamp=" + timestamp;
  } else {
    cre = "api_key=" + access_key + "&" + param + "&timestamp=" + timestamp;
  }
  return ApiSignatureUtil::GetSignatureByHexHmacSHA256(cre, secret_key);
}

std::string BybitWsAuth::CreateSignature() const {
  std::list<std::string> out;
  std::string ts_str = std::to_string(GetCurrentTimestamp() / 1000000 + 1000);
  std::string signa =
      ApiSignatureUtil::GetSignatureByHexHmacSHA256("GET/realtime" + ts_str, key_.secret_key());
  out.emplace_back(key_.access_key());
  out.emplace_back(ts_str);
  out.emplace_back(signa);

  JsonWriter writer;
  writer.PutString("op", "auth");
  writer.PutArray("args", out);
  return writer.ToJsonString();
}

AsioHttpContext* BybitRestAuth::CreateRequestByGetWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string ts_str = GetCurrTimeSeconds();
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = builder->ComputePostBody();
  CHECK(post_body.empty());
  std::string signa = GetSignature(key_.access_key(), key_.secret_key(), ts_str, query_string);

  std::string target;
  if (!query_string.empty()) {
    target = address + "?" + "api_key=" + key_.access_key() + "&" + query_string +
             "&timestamp=" + ts_str + "&sign=" + signa;
  } else {
    target =
        address + "?" + "api_key=" + key_.access_key() + "&timestamp=" + ts_str + "&sign=" + signa;
  }

  return AsioHttpContext::NewGet(host_, port_, bind_ip_, target);
}

AsioHttpContext* BybitRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string ts_str = GetCurrTimeSeconds();
  std::string query_string = builder->ComputePostBodyAsQueryString();
  std::string signa = GetSignature(key_.access_key(), key_.secret_key(), ts_str, query_string);

  builder->PutPost("api_key", key_.access_key());
  builder->PutPost("timestamp", ts_str);
  builder->PutPost("sign", signa);
  std::string post_body = builder->ComputePostBody();
  boost::replace_all(post_body, R"("true")", "true");
  boost::replace_all(post_body, R"("false")", "false");

  return AsioHttpContext::NewPost(host_, port_, bind_ip_, address, post_body);
}
